__author__ = 'Muscle'

import traceback
import time
from mui.muievent import MUIEventDispatcher
from mui.muicomponent import *

from mui.CExt.CW32Window import *
import mui.CExt.CUser32 as CUser32
import mui.CExt.muiDX as muiDX
import mui.muiApp as muiApp
import mui.InternalUse.muiconfig as config

from mui.muirender import MUIRenderer

from mui.muicomponent import _MOUSE_MSGTYPE_
from mui.muicomponent import _KEY_MSGTYPE_
from mui.muicomponent import _TEXT_MSGTYPE_
from mui.muicomponent import _MOUSE_BUTTON_

from mui.Widgets.muitooltips import MUITooltips
from mui.Widgets.muidialog import PopupBase

import mui.muianim as anim
from mui.InternalUse.muicontainer import MUIList
from muikeys import keyStr, keyStr2Symbol


class MUIWindow(MUIComponent, IWindow):
    def __init__(self, *args, **kwargs):
        self._dialogs = MUIList()
        self._popups = MUIList()     # all domodal dialog will save here
        self.owner = kwargs.get('owner', None)
        kwargs.update(left = 0, top = 0)
        #######
        self._create_w32window(**kwargs)
        self._bind_w32window_event()
        self._create_rendering_device(**kwargs);
        ######
        MUIComponent.__init__(self, None, **kwargs)
        #property
        self.tabStop = False

        #add self to the window list of eventloop
        appLoop = muiApp.getEventLoop()
        appLoop.windows.add(self)
        #for input message (keyboard / mouse)
        self._modifiers = 0
        self._buttons = 0
        self._isMouseInWindow = False


        self.bind('Wnd Destory', appLoop.on_window_close)
        self.bind("on_paint", self.on_paint, noarg = True)
        #-------------------------------------------
        # for drag drop
        self.preDragDropComponent = None    # click left button down and prepare to dragdrop (start after mouse moving for a large range)
        self.dragDroping = False    # in drag drop mode
        self.dragDropStartPos = -1, -1
        self.cursor = 'wait'
        self.cursorIsShown = True
        #self.changeEvent('cursor', self.__onChangeCursor, postevent=False)

        self.oleDropData = None
        self.muiDropData = None
        self._draggingThumb = None

        #-------------------------------------------------
        # tool tips
        self.tooltips = MUITooltips(self)
        self.tooltips.bindData('maxWidth', self, 'width', dir='<-', converter=lambda w: w - self.tooltips.border[0])
        self.tooltips.visible = False

        self.addAcceleratorKey('ALT ENTER', 'Change FullScreen')
        self.bindCmd('Change FullScreen', self._changeFullScreen)

    def _changeFullScreen(self):
        if (self.isFullScreen):
            self.setFullScreen(False, True, True)
        else:
            self.setFullScreen(True, True, True)
            
    def __syncSize(self):
        try:
            if not hasattr(self, 'swapChain'):
                return
            rect = muiDX.RECT()
            self._w32window.GetClientRect(rect)
            w = (int)(rect.right - rect.left)
            h = (int)(rect.bottom - rect.top)
            self.width = w
            self.height = h
            self.swapChain.ResizeBuffer(w, h)
            self.renderer.UpdateViewportByFrameBuf()
            self.renderer.UpdateCamViewByViewport()

        except Exception, e:
            traceback.print_exc()
            return None

    def _create_w32window(self, **kwargs):
        try:
            hwndParent = 0
            if (self.owner):
                hwndParent = self.owner.hwnd
            winX = kwargs.get('winPosX', CW_USEDEFAULT)
            winY = kwargs.get('winPosY', CW_USEDEFAULT)
            winW = kwargs.get('width', 640)
            winH = kwargs.get('height', 480)
            wtitle = kwargs.get('title', u'MUIWindow')
            classStyle = kwargs.get('classStyle', CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS)
            windowStyle = kwargs.get('windowStyle', WS_MINIMIZEBOX| WS_MAXIMIZEBOX | WS_OVERLAPPEDWINDOW)
            windowExStyle = kwargs.get('windowExStyle', WS_EX_APPWINDOW | WS_EX_ACCEPTFILES | WS_EX_LAYERED)
            self._w32window = CW32Window()
            self._w32window.Create(hwndParent, winW, winH, wtitle, classStyle, windowStyle, windowExStyle, winX, winY)
            self._w32window.SetLayeredWindowAttributes(1.0)
            self.hwnd = self._w32window.GetHwnd()

        except Exception, e:
               traceback.print_exc()
               return None

    def _create_rendering_device(self, **kwargs):
        try:
            dxDevice = kwargs.get('dxDevice', None)
            if (dxDevice == None):
                if (self.owner and self.owner.dxDevice):
                    dxDevice = self.owner.dxDevice
            if (dxDevice == None):
                dxDevice = muiDX.CDXDevice()
            else:
                dxDevice.AddRef()

            self.dxDevice = dxDevice

            self.swapChain = muiDX.CDXSwapChain(self.dxDevice)
            self.swapChain.Create(self.hwnd)

            self.renderer = MUIRenderer(self.dxDevice)
            self.renderer.SetFrameBuffer(self.swapChain)

            self.dxDevice.MakeWindowAssociation(self.hwnd, muiDX.DXGI_MWA_NO_ALT_ENTER)

        except Exception, e:
               traceback.print_exc()
               return None

    def _bind_w32window_event(self):
        try:
            #window prop change
            self._w32window.SetCallback("ENTERSIZEMOVE", self.on_enter_size_move)
            self._w32window.SetCallback("EXITSIZEMOVE", self.on_exit_size_move)

            self._w32window.SetCallback("MOVE", self.on_move )
            self._w32window.SetCallback("MOVING", self.on_moving)
            self._w32window.SetCallback("SHOWWINDOW", self.on_show_window)
            self._w32window.SetCallback("SIZE", self.on_size)
            self._w32window.SetCallback("SIZING", self.on_sizing)
            self._w32window.SetCallback("WINDOWPOSCHANGED", self._on_win_pos_changed)
            self._w32window.SetCallback("WINDOWPOSCHANGEING", self._on_win_pos_changing)

            #window life
            self._w32window.SetCallback("NCACTIVATE", self.on_ncactivate)
            self._w32window.SetCallback("ACTIVATE", self.on_activate)
            self._w32window.SetCallback("CLOSE", self.on_close)
            self._w32window.SetCallback("CREATE", self._on_create)
            self._w32window.SetCallback("DESTORY", self.on_destory)
            self._w32window.SetCallback("NCDESTORY", self.on_ncdestory)
            self._w32window.SetCallback("ENABLE", self.on_enable)
            self._w32window.SetCallback("QUIT", self.on_quit)
            #keyboard message
            self._w32window.SetCallback("KEYDOWN", self.on_key_down)
            self._w32window.SetCallback("KEYUP", self.on_key_up)
            self._w32window.SetCallback("CHAR", self.on_char)

            #mouse message
            self._w32window.SetCallback("MOUSEMOVE", self.on_mouse_move)
            self._w32window.SetCallback("MOUSEWHEEL", self.on_mouse_wheel)
            self._w32window.SetCallback("LBUTTONDOWN", self.on_mouse_lbutton_down)
            self._w32window.SetCallback("LBUTTONUP", self.on_mouse_lbutton_up)
            self._w32window.SetCallback("LBUTTONDBLCLK", self.on_mouse_lbutton_dblclick)
            self._w32window.SetCallback("RBUTTONDOWN", self.on_mouse_rbutton_down)
            self._w32window.SetCallback("RBUTTONUP", self.on_mouse_rbutton_up)
            self._w32window.SetCallback("RBUTTONDBLCLK", self.on_mouse_rbutton_dblclick)
            self._w32window.SetCallback("MBUTTONDOWN", self.on_mouse_mbutton_down)
            self._w32window.SetCallback("MBUTTONUP", self.on_mouse_mbutton_up)
            self._w32window.SetCallback("MBUTTONDBLCLK", self.on_mouse_mbutton_dblclick)
            self._w32window.SetCallback("MOUSELEAVE", self._on_mouse_leave)

            #never used message
            self._w32window.SetCallback("CANCELMODE", self.on_cancel_mode)
            self._w32window.SetCallback("GETMINMAXINFO", self.on_get_minmaxinfo)
            #drag and drop
            self._w32window.SetCallback("QUERYDRAGICON", self.on_query_drag_icon)
            self._w32window.SetCallback("QUERYDRAGOPEN", self.on_query_drag_open)
            #apparent change
            #self._w32window.SetCallback("PAINT", self.on_paint)
            self._w32window.SetCallback("STYLECHANGED", self._on_style_changed)
            self._w32window.SetCallback("STYLECHANGEING", self._on_style_changing)
            self._w32window.SetCallback("THEMECHANGED", self.on_theme_changed)
            self._w32window.SetCallback("USERCHANGED", self.on_user_changed)
            ########
            #######Drag and Drop#############
            self._w32window.SetCallback("COMPLETE_DROP", self.on_complete_drop)
            self._w32window.SetCallback("QueryDropEffect", self.on_query_drop_effect)
            self._w32window.SetCallback("onOleDropFiles", self.on_ole_dropfiles)

        except Exception, e:
               traceback.print_exc()
               return None

    def __del__(self):
        if (config.g_bPrintDbg):
            print 'MUIWindow __del__ called!!'
        self._w32window.Release()
        self._w32window = None

    def ShowWindow(self, bShow):
        try:
            self._w32window.ShowWindow(bShow)
        except Exception, e:
               traceback.print_exc()
               return None

    def EnableWindow(self, bEnable):
        try:
            self._w32window.EnableWindow(bEnable)
        except Exception, e:
               traceback.print_exc()
               return None

    def DestroyWindow(self):
        try:
            self._w32window.DestroyWindow()
        except Exception, e:
            traceback.print_exc()
            return None


    def SetCursor(self, strCursor):
        try:
            self._w32window.SetCursor(unicode(strCursor))
        except Exception, e:
            traceback.print_exc()
            return None

    def LoadCursor(self, strCursor, strCursorPath):
        try:
            self._w32window.LoadCursor(unicode(strCursor), unicode(strCursorPath))
        except Exception, e:
            traceback.print_exc()
            return None

    def SetCapture(self, bCaptured):
        try:
            self._w32window.SetCapture(bCaptured)
        except Exception, e:
            traceback.print_exc()
            return None
    def setFocus(self, hint = '', **kwargs):
        try:
            self._w32window.SetFocus()
            super(MUIWindow, self).setFocus(hint, **kwargs)
        except Exception, e:
            traceback.print_exc()
            return None


    def calculateDirtyRects(self):
        try:
            def getDirtyRect(node):
                rectRet = []
                if not node.visible:
                    return []
                if node.dirty:
                    rectRet.append(Rectangle(*node.dirtyRect))
                    if node._preDirtyRect:
                        rectRet.append(Rectangle(*node._preDirtyRect))

                if (not node.bClip or not node.dirty):
                    for c in node.children:
                        cDirtyRect = getDirtyRect(c)
                        if (cDirtyRect):
                            rectRet += cDirtyRect

                if (hasattr(node, '_dialogs') ): #node is MUIWindow
                    for c in node._dialogs:
                        if (isinstance(c, IWindow)):
                            continue
                        
                        cDirtyRect = getDirtyRect(c)
                        if (cDirtyRect):
                            rectRet += cDirtyRect

                #union dirtyRect
                unionRet = []
                if (len(rectRet) > 1):
                    while(len(rectRet)):
                        r = rectRet[0]
                        bIntersect = False
                        for i in range(1, len(rectRet)):
                            if (r.IsIntersect(rectRet[i])):
                                rectRet[0] = r.union(rectRet[i])
                                rectRet.pop(i)
                                bIntersect = True
                                break
                        if (not bIntersect):
                            unionRet.append(r)
                            rectRet.pop(0)
                else:
                    unionRet = rectRet
                return unionRet

            def updateDirtyRect(node, checkRects):
                node._dirtyRect = []
                if (not node.dirty and node.visible):
                    gRect = Rectangle(*node.globalRect)
                    for r in checkRects:
                        if (gRect.IsIntersect(r)):
                            intersecR = gRect.intersect(r)
                            node._dirtyRect.append(intersecR)
            
                for c in node.children:
                    updateDirtyRect(c, checkRects)

                if (hasattr(node, '_dialogs') ): #node is MUIWindow
                    for c in node._dialogs:
                        updateDirtyRect(c, checkRects)
            allDirtyRect = getDirtyRect(self)
            updateDirtyRect(self, allDirtyRect)

        except Exception, e:
            traceback.print_exc()
            return None


    def on_paint(self):
        try:
            if (0 and config.g_bPrintDbg):
                print 'on_paint'
            if (not self.renderer):
                return
            self.calculateDirtyRects()

            self.renderer.BeginDraw()
            if (config.g_bUseDirtyRegion):
                if (self.dirty):
                    self.renderer.ClearColor((0.0, 0.0, 0.0, 1.0))
                else:
                    if (hasattr(self, '_dirtyRect') and self._dirtyRect):
                        for dRect in self._dirtyRect:
                            localLT = self.global2local(dRect.rect[0], dRect.rect[1])
                            clipRect = (localLT[0], localLT[1],  localLT[0] + dRect.rect[2], localLT[1] + dRect.rect[3])
                            self.renderer.PushAxisAlignedClip(clipRect)
                            self.renderer.ClearColor((0.0, 0.0, 0.0, 1.0))
                            self.renderer.PopAxisAlignedClip()
                            #print clipRect
                            #_cRect = (clipRect[0] + 4, clipRect[1] + 4, clipRect[2] - 4, clipRect[3] - 4)
                            #self.renderer.DrawColorRectangle(_cRect, (1.0, 0.0, 0.0, 1.0), 2)
            else:
                self.renderer.ClearColor((0.0, 0.0, 0.0, 1.0))
            self.renderer.PushMatrix()
            self.renderer.LoadIdentity()
            self.passDraw(self.renderer)
            self.renderer.PopMatrix()

            self.renderer.EndDraw()
            self.swapChain.Present()

        except Exception, e:
            traceback.print_exc()
            return None

    def on_key_down(self, symbol, modifiers):
        try:
            self._key_modifiers = modifiers
            key = keyStr(symbol, modifiers)
            if key == 'ALT ENTER':
                test = 0
            if (config.g_bPrintDbg):
                print 'key down', symbol, modifiers
            if self.enabled and self.visible:
                self.passKey(_KEY_MSGTYPE_.press, key, symbol, modifiers)
            self.dispatch_event('Window KeyDown', self, key, symbol, modifiers)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_key_up(self, symbol, modifiers):
        try:
            self._key_modifiers = modifiers
            key = keyStr(symbol, modifiers)
            if (config.g_bPrintDbg):
                print 'key up', symbol, modifiers
            if self.enabled and self.visible:
                self.passKey(_KEY_MSGTYPE_.release, key, symbol, modifiers)

            self.dispatch_event('Window KeyUp', self, key, symbol, modifiers)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_char(self, text, modifiers):
        try:
            self._key_modifiers = modifiers
            if (config.g_bPrintDbg):
                print 'on char', text,  modifiers
            self.passText(_TEXT_MSGTYPE_.text)
            self.dispatch_event('Window Char', self, text, modifiers)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_enter_size_move(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_enter_size_move'
            self.dispatch_event('Window EnterSizeMove', self)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_exit_size_move(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_exit_size_move'
            self.__syncSize()
            self.dispatch_event('Window ExitSizeMove', self)
            self.dispatch_event('on_paint')

        except Exception, e:
            traceback.print_exc()
            return None

    def on_move(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_move', x, y
            self.dispatch_event('Window Move', self)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_moving(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_moving', x, y
            self.dispatch_event('Window Moving', self, x, y)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_show_window(self, bShow, status):
        try:
            if (config.g_bPrintDbg):
                print 'on_show_window', bShow, status
            if (bShow):
                self.setDirty(True)
            self.dispatch_event('Window Show', self, bShow)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_size(self, w, h, flag):
        try:
            if (config.g_bPrintDbg):
                print 'on_size', w, h, flag
            self.__syncSize()
            self.setDirty(True)
            self.dispatch_event('Window Resize', self, w, h, flag)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_sizing(self, w, h, flag):
        try:
            if (config.g_bPrintDbg):
                print 'on_sizing', w, h, flag
            self.dispatch_event('Window Resizing', self, w, h, flag)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_win_pos_changed(self, wpos):
        try:
            if (config.g_bPrintDbg):
                print 'on_win_pos_changed', wpos
            self.__syncSize()
            self.dispatch_event('on_paint')
        except Exception, e:
            traceback.print_exc()
            return None

    def _on_win_pos_changed(self, _wpos):
        try:
            wpos = WINDOWPOS(_wpos)
            self.on_win_pos_changed(wpos)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_win_pos_changing(self, wpos):
        try:
            if (config.g_bPrintDbg):
                print 'on_win_pos_changed', wpos
        except Exception, e:
            traceback.print_exc()
            return None

    def _on_win_pos_changing(self, _wpos):
        try:
            wpos = WINDOWPOS(_wpos)
            self.on_win_pos_changing(wpos)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_ncactivate(self, bActivate, hRgn):
        try:
            if (config.g_bPrintDbg):
                print 'on_ncactivate'
        except Exception, e:
            traceback.print_exc()
            return None


    def on_activate(self, actType, bMinimize, hwnd):
        try:
            if (config.g_bPrintDbg):
                print 'on_activate'
            self.setDirty(True)
            self.dispatch_event('Activate Wnd', actType, bMinimize, hwnd)
            self.dispatch_event('on_paint')
        except Exception, e:
            traceback.print_exc()
            return None
        

    def on_close(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_close'
        except Exception, e:
            traceback.print_exc()
            return None

    def on_create(self, createInfo):
        try:
            if (config.g_bPrintDbg):
                print 'on_create', createInfo
            self.__syncSize()
            self.setDirty(True)

        except Exception, e:
            traceback.print_exc()
            return None

    def _on_create(self, _createInfo):
        try:
            createInfo = CREATESTRUCT(_createInfo)
            self.on_create(createInfo)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_destory(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_destory'

            self.dispatch_event('Wnd Destory', self)
            self._w32window.ClearCallback()
            self.clear()
            if (hasattr(self, "dxDevice")):
                self.dxDevice.Release()
                self.dxDevice = None
            if (hasattr(self, "swapChain")):
                self.swapChain.Release()
                self.swapChain = None
            if (hasattr(self, "renderer")):
                self.renderer.Release()
                self.renderer = None

            if (not self.dead):
                self.close()
        except Exception, e:
            traceback.print_exc()
            return None
        
    def on_ncdestory(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_ncdestory'
        except Exception, e:
            traceback.print_exc()
            return None

    def on_enable(self, bEnable):
        try:
            if (config.g_bPrintDbg):
                print 'on_enable', bEnable
            self.setDirty(True)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_quit(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_quit'
        except Exception, e:
            traceback.print_exc()
            return None


    def on_mouse_move(self, x, y, dx, dy):
        try:
            if (not self._isMouseInWindow):
                self._isMouseInWindow = True
                self.on_mouse_enter(x, y)

            #print 'on_mouse_move', x, y, dx, dy
            self.handle_mouse_move(x, y, dx, dy)

            #self.passMouseMsg(_MOUSE_MSGTYPE_.motion, x, y, dx = dx, dy = dy, modifiers = self._modifiers)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_wheel(self, x, y, zdelta, keyState):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_wheel', x, y, zdelta, keyState
            self.DispatchMessage('passMouseMsg', _MOUSE_MSGTYPE_.scroll, x, y, buttons = self._buttons, modifiers = self._modifiers, dz = zdelta)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_lbutton_down(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_lbutton_down', x, y
            self._buttons = self._buttons | _MOUSE_BUTTON_.LEFT
            self.invoke('Window Mouse Down', x, y)
            self.DispatchMessage('passMouseMsg', _MOUSE_MSGTYPE_.press, x, y, buttons = _MOUSE_BUTTON_.LEFT, modifiers = self._modifiers)
            #self.passMouseMsg(_MOUSE_MSGTYPE_.press, x, y, buttons = _MOUSE_BUTTON_.LEFT, modifiers = self._modifiers)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_lbutton_up(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_lbutton_up', x, y
            self._buttons = self._buttons ^ _MOUSE_BUTTON_.LEFT

            self.invoke('Window Mouse Up', x, y)

            if self.dragDroping:
                self.DispatchMessage('passQueryDrop', x, y, 'DROP', None, self.muiDropData)
                self.endDragDrop()
            else:
                if self.preDragDropComponent:
                    assert self.dragDroping == False
                    self.preDragDropComponent = None
                if MUIComponent.mouseDownIn:
                    self.DispatchMessage('passMouseMsg', _MOUSE_MSGTYPE_.release, x, y, buttons = _MOUSE_BUTTON_.LEFT, modifiers = self._modifiers)

            if MUIComponent.mouseDownIn:
                #print '[window] State error, fix it\n'
                MUIComponent.mouseDownIn = None


            #self.passMouseMsg(_MOUSE_MSGTYPE_.release, x, y, buttons = _MOUSE_BUTTON_.LEFT, modifiers = self._modifiers)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_lbutton_dblclick(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_lbutton_dblclick', x, y
            self.passMouseMsg(_MOUSE_MSGTYPE_.dblclick, x, y, buttons = _MOUSE_BUTTON_.LEFT, modifiers = self._modifiers)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_rbutton_down(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_rbutton_down', x, y
            self._buttons = self._buttons | _MOUSE_BUTTON_.RIGHT
            self.passMouseMsg(_MOUSE_MSGTYPE_.press, x, y, buttons = _MOUSE_BUTTON_.RIGHT, modifiers = self._modifiers)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_rbutton_up(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_rbutton_up', x, y
            self._buttons = self._buttons ^ _MOUSE_BUTTON_.RIGHT
            self.passMouseMsg(_MOUSE_MSGTYPE_.release, x, y, buttons = _MOUSE_BUTTON_.RIGHT, modifiers = self._modifiers)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_rbutton_dblclick(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_rbutton_dblclick', x, y

            self.passMouseMsg(_MOUSE_MSGTYPE_.dblclick, x, y, buttons = _MOUSE_BUTTON_.RIGHT, modifiers = self._modifiers)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_mbutton_down(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_mbutton_down', x, y
            self._buttons = self._buttons | _MOUSE_BUTTON_.MIDDLE
            self.passMouseMsg(_MOUSE_MSGTYPE_.press, x, y, buttons = _MOUSE_BUTTON_.MIDDLE, modifiers = self._modifiers)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_mbutton_up(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_mbutton_up', x, y
            self._buttons = self._buttons ^ _MOUSE_BUTTON_.MIDDLE
            self.passMouseMsg(_MOUSE_MSGTYPE_.release, x, y, buttons = _MOUSE_BUTTON_.MIDDLE, modifiers = self._modifiers)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_mbutton_dblclick(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_mbutton_dblclick', x, y
            self.passMouseMsg(_MOUSE_MSGTYPE_.dblclick, x, y, buttons = _MOUSE_BUTTON_.MIDDLE, modifiers = self._modifiers)

        except Exception, e:
            traceback.print_exc()
            return None

    def on_mouse_enter(self, x, y):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_enter', x, y
            if self.tooltips:
                self.tooltips.enabled = True

        except Exception, e:
            traceback.print_exc()
            return None


    def on_mouse_leave(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_mouse_leave'

            if self.tooltips:
                self.tooltips.enabled = False
                self.hideToolTips()

        except Exception, e:
            traceback.print_exc()
            return None


    def _on_mouse_leave(self):
        try:
            self._isMouseInWindow = False
            self.on_mouse_leave()

        except Exception, e:
            traceback.print_exc()
            return None

    def on_cancel_mode(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_cancel_mode'
        except Exception, e:
            traceback.print_exc()
            return None

    def on_get_minmaxinfo(self, info):
        try:
            if (config.g_bPrintDbg):
                print 'on_get_minmaxinfo', info
        except Exception, e:
            traceback.print_exc()
            return None
    def _on_get_minmaxinfo(self, _info):
        try:
            info = MINMAXINFO(_info)
            self.on_get_minmaxinfo(info)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_query_drag_icon(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_query_drag_icon', info
        except Exception, e:
            traceback.print_exc()
            return None

    def on_query_drag_open(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_query_drag_open'
        except Exception, e:
            traceback.print_exc()
            return None

    def on_style_chagned(self, styleType, styleInfo):
        try:
            if (config.g_bPrintDbg):
                print 'on_style_changed', styleType, styleInfo
            self.setDirty(True)
        except Exception, e:
            traceback.print_exc()
            return None

    def _on_style_changed(self, styleType, _styleInfo):
        try:
            styleInfo = STYLESTRUCT(_styleInfo)
            self.on_style_chagned(styleType, styleInfo)
        except Exception, e:
            traceback.print_exc()
            return None


    def on_style_changing(self, styleType, styleInfo):
        try:
            if (config.g_bPrintDbg):
                print 'on_style_changing', styleType, styleInfo
        except Exception, e:
            traceback.print_exc()
            return None

    def _on_style_changing(self, styleType, _styleInfo):
        try:
            styleInfo = STYLESTRUCT(_styleInfo)
            self._on_style_changing(styleType, styleInfo)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_theme_changed(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_theme_changed'
            self.setDirty(True)
        except Exception, e:
            traceback.print_exc()
            return None

    def on_user_changed(self):
        try:
            if (config.g_bPrintDbg):
                print 'on_user_changed'
            self.setDirty(True)
        except Exception, e:
            traceback.print_exc()
            return None

    def hideToolTips(self):
        if hasattr(self, 'tooltips') and self.tooltips and self.tooltips.visible:
            #print '----------- hide tips'
            self.tooltips.text = ''
            self.tooltips.visible = False
           


    def showToolTips(self, text, xy):
        if hasattr(self, 'tooltips'):
            tooltips = self.tooltips
            if tooltips and not tooltips.visible and tooltips.enabled:
                #print '----------- show tips', text, xy
                tooltips.text = text
                tooltips.updateTextLayout('text')
                self.resetToolTipsPosition(xy)
                tooltips.visible = True

    def resetToolTipsPosition(self, (x, y)):
        ox, oy = 10, 10          # offset x, y
        if hasattr(self, 'tooltips') and self.tooltips:
            w, h = self.tooltips.size
            if h > self.height:
                self.tooltips.top = 0
            elif y+oy+h < self.height:
                self.tooltips.top = y+oy
            elif y-oy-h < 0:
                self.tooltips.bottom = self.height
            else:
                self.tooltips.bottom = y-oy

            if w > self.width:
                self.tooltips.left = 0
            elif x+ox+w < self.width:
                self.tooltips.left = x+ox
            elif x-ox-w < 0:
                self.tooltips.right = self.width
            else:
                self.tooltips.right = x-ox

    ###drag and drop related function
    # override drawChild to draw the dragging thumb
    def drawChild(self, render):
        for c in self._children:
            if (c == self.tooltips or c == self._draggingThumb):
                continue
            c.passDraw(render)

        map(lambda c: c.passDraw(render), self._dialogs)

        if not self.parent:
            draggingThumb = self._draggingThumb
            if self.dragDroping and draggingThumb != None:
                draggingThumb.passDraw(render)
            if self.tooltips:
                self.tooltips.passDraw(render)
                
    def on_complete_drop(self):
        return 1

    def on_query_drop_effect(self, x, y):
        ret = self.passQueryDrop(x, y, 'Query')
        if (ret):
            return 1
        else:
            return 0

    def on_ole_dropfiles(self, x, y, _files):
        print 'on_ole_dropfiles'
        files = _files.split('*')
        self.DispatchMessage('passQueryDrop', x, y, 'DROP', files, None)
        
    def prepareDragDrop(self, x, y, obj, draggingThumb = None):
        try:
            if (config.g_bPrintDbg):
                print 'muiwindow.py prepareDragDrop'
            self.preDragDropComponent = obj
            self.dragDropStartPos = x, y
            if draggingThumb != None:
                if self._draggingThumb != None:
                    self._draggingThumb.close()
                self._draggingThumb = draggingThumb
                if (self._draggingThumb):
                    self._draggingThumb.passThrough = True #in case it block the mouse message
                    self._draggingThumb.left = x
                    self._draggingThumb.top = y

                self.setDirty(True)
        except Exception, e:
            traceback.print_exc()
            return None

    def beginDragDrop(self):
        try:
            if (config.g_bPrintDbg):
                print 'muiwindow.py beginDragDrop'
            if self.preDragDropComponent:
                preDragDropComponent = self.preDragDropComponent
                self.dropMode = self.preDragDropComponent.dropMode
                self.preDragDropComponent = None
                self.dragDroping = True

                #print '[window.py] beginDragDrop', self.preDragDropComponent
                if self.dropMode == 'ole':
                    self.oleDropData = preDragDropComponent.onQueryOleDropData()
                self.muiDropData = preDragDropComponent.onQueryMUIDropData()
                self.SetCursor('query')

                self.SetCapture(True)
        except Exception, e:
            traceback.print_exc()
            return None

    def endDragDrop(self, clearData = True):
        try:
            if (config.g_bPrintDbg):
                print 'muiwindow.py endDragDrop'
            assert self.preDragDropComponent == None
            self.SetCursor('arrow')
           
            self.SetCapture(False)
            self.dragDroping = False
            self.dropMode = None
            if clearData:
                self.oleDropData = None
                self.muiDropData = None
            if self._draggingThumb != None:
                if self._draggingThumb != None:
                    self._draggingThumb.close()
                self._draggingThumb = None
                self.dispatch_event('on_paint')

        except Exception, e:
            traceback.print_exc()
            return None


    def passMouseMsgToChildren(self, msgType, x, y, **kwargs):
        #---------------------------------------------
        # check if any child is focus
        #---------------------------------------------
        for c in self.children[::-1]:
            # 1. last time is over or mouse down, we should focus to recheck again
            # 2. check only if child pass hitTest
            #if c not in self._popups and c.visible and c.enabled and not c.blind:
            if c not in self._popups and c.visible and not c.blind:   # disabled control still should pass mouse event to child
                _x, _y = c.parent2local(x, y)
                if c.passMouseMsg( msgType, _x, _y, **kwargs):
                    return True
        return False

    def passMouseMsg(self, msgType, x, y, **kwargs):
        if self._popups:
            for c in self._popups[::-1]:
                assert isinstance(c, PopupBase)
                if not c.visible:
                    continue
                _x, _y = c.parent2local(x, y)
                ret = c.passMouseMsg(msgType, _x, _y, **kwargs)
                #if ret or not isinstance(c, PopupMenu):
                return ret
        return super(MUIWindow, self).passMouseMsg( msgType, x, y, **kwargs)

    def passQueryDrop(self, x, y, state, *args):
        if self._popups:
            for c in self._popups[::-1]:
                if not c.visible:
                    continue
                _x, _y = c.parent2local(x, y)
                ret = c.passQueryDrop(_x, _y, state, *args)
                if ret or isinstance(c, PopupMenu):
                    return ret
            return self.handleQueryDrop(x, y, state, *args)

        ret = super(MUIWindow, self).passQueryDrop(x, y, state, *args)
        # When mouse is out side of window,
        # no one would set the cursor for non-ole drop mode.
        # Therefore window should set the cursor to "forbidden"
        if state == 'QUERY' and not ret:
            self.SetCursor('query')
        return ret

    def DispatchMessage(self, func, *arg, **kwargs):
        """
        dispatch messages

        @param func: message type, one of:
                    1. mouse message
                    2. key event
                    3. command
        @type func: string
        @param arg: additional arguments
        @rtype: None
        """
        try:
            if func == 'passMouseMsg':
                msgType, x, y = arg[0], arg[1], arg[2],
                self.preMouseMoveArg = msgType, x, y, kwargs

                if MUIComponent.mouseCapture:
                    _x, _y = MUIComponent.mouseCapture.window2local( x, y )
                    return MUIComponent.mouseCapture.passMouseMsg(msgType, _x, _y, **kwargs)
                else:
                    if self.blind:
                        return False
                    _x, _y = self.window2local(x, y)
                    return self.passMouseMsg(msgType, _x, _y, **kwargs)

            elif func == 'passQueryDrop':
                x, y = arg[0], arg[1]
                if MUIComponent.mouseCapture:
                    _x, _y = MUIComponent.mouseCapture.window2local(arg[0], arg[1])
                    return MUIComponent.mouseCapture.passQueryDrop(_x, _y, state)
                else:
                    if self.blind:
                        return False
                    _x, _y = self.window2local(arg[0], arg[1])
                    return self.passQueryDrop(_x, _y, *arg[2:])

            elif func == 'passKey':
                if self.blind:
                    return False
                key, symbol, modifiers = arg[0], arg[1], arg[2]

                return self.passKey( _KEY_MSGTYPE_.press, key, symbol, modifiers)
            else:
                f = getattr(self, func)
                f(*arg)

        except Exception:
            traceback.print_exc()
            return False


    def handle_mouse_move(self, x, y, dx, dy):
        try:
           self.invoke('Window Mouse Move', x, y, dx, dy)

           if self.preDragDropComponent:
               if abs(self.dragDropStartPos[0] - x) > 10 or abs(self.dragDropStartPos[1] - y) > 10:
                   self.beginDragDrop()
           elif self.dragDroping:
               pass # query drag drop

           #if self.inputmethod == 'MOUSE':
           if self.dragDroping:
               ret = self.DispatchMessage('passQueryDrop', x, y, 'QUERY')
               if ret == False and DragDropBase.dragOver:
                   DragDropBase.dragOver.isDragOver = False
                   DragDropBase.dragOver.invoke('Drag Leave')
                   DragDropBase.dragOver = None
           else:
               ret = self.DispatchMessage('passMouseMsg', _MOUSE_MSGTYPE_.motion, x, y, dx = dx, dy = dy, modifiers = self._modifiers)
               if ret == False and MUIComponent.mouseOver:
                   MUIComponent.mouseOver.isMouseOver = False
                   MUIComponent.mouseOver.invoke('Mouse Leave', MUIComponent.mouseOver, x, y )
                   MUIComponent.mouseOver = None

           # support dragging thumb
           if self._draggingThumb != None:
               if self.dragDroping:
                   _x, _y = self.parent2local(x, y)

                   animobjX = anim.AnimDecay([0, 1.0], [self._draggingThumb.left, _x], 0.1)
                   animTaskX = anim.AnimTask(self._draggingThumb, 'left', animobjX, bAllowInterrupt = True)
                   animTaskX.start()
                   animobjY = anim.AnimDecay([0, 1.0], [self._draggingThumb.top, _y], 0.1)
                   animTaskY = anim.AnimTask(self._draggingThumb, 'top', animobjY, bAllowInterrupt = True)
                   animTaskY.start()

           if x < 0 or y < 0 or x > self.width or y > self.height:
               if self.dragDroping and self.dropMode == 'ole':
                   self.onStartOleDragDrop()

        except Exception, e:
           traceback.print_exc()
           return None

    def onStartOleDragDrop(self):
        if self.oleDropData != None and len(self.oleDropData):
            files = u''
            for f in self.oleDropData:
                files += (unicode(f)+u'\0')
            files += u'\0'
            if self.muiDropData != None:
                import cPickle
                muiDropData = cPickle.dumps(self.muiDropData)
                self.endDragDrop(False)
                self._w32window.DoDragDrop(files, muiDropData, True)
            else:
                self.endDragDrop(False)
                self._w32window.DoDragDrop(files, len(files), None, 0, True)
            self.oleDropData = None
            self.muiDropData = None

    #for popup dialog
    def _removeChild(self, c):
        if c.parent == self:
            c._parent = None

        if self.focusChild == c:
            self.focusChild = None

        children = self._dialogs if isinstance(c, PopupBase) else self._children

        if c in children:
            self.invoke('Child Remove', c)
            children.remove(c)

        if self.focusChild is None and self.isFocused():
            self.guessFocusChild()

    def _addChild(self, c, **argd):
        if c in self._children or c in self._dialogs:
            raise TypeError

        children = self._dialogs if isinstance(c, PopupBase) else self._children

        if argd.get('front', False):
            children.insert(0, c)
        else:
            children.append(c)

        if not self.focusChild:
            self.focusChild = c
        self.invoke('Child Add', c)


    def getTabChildren(self):
        children = []
        if self._popups:
            children = self._popups[-1].tabChildren
        else:
            dialogs = filter(lambda x: x.focus and x.visible and x.enabled and not x.blind, self._dialogs)
            if dialogs:
                children = dialogs[-1].tabChildren
            else:
                for c in self._children:
                    if c.visible and c.enabled and not c.blind:
                        if c.tabStop:
                            children.append(c)
                        children += c.tabChildren
                if children:
                    children.sort(lambda x, y: cmp(x.tabStop, y.tabStop))
        return children
    tabChildren = property(getTabChildren)

    def local2screen(self, x, y):
        try:
            hr, sx, sy = self._w32window.ClientToScreen(x, y)
            return sx, sy

        except Exception, e:
            traceback.print_exc()
            return None

    def screen2local(self, sx, sy):
        try:
            windowRect = RECT()
            hr, wx, wy = self._w32window.ScreenToClient(sx, sy)
            return wx, wy

        except Exception, e:
            traceback.print_exc()
            return None

    def _getIsFullScreen(self):
        try:
            return self._w32window.IsFullScreen()
        except Exception, e:
            traceback.print_exc()
            return None

    isFullScreen = property(_getIsFullScreen)

    def enterMovingMode(self):
        if not self.isFullScreen:
            self._w32window.EnterMovingMode()

    def setFullScreen(self, bFullScreen, bShow, bActivate):
        try:
            if (isinstance(self, PopupBase)):
                return 
            return self._w32window.SetFullScreen(bFullScreen, bShow, bActivate)

        except Exception, e:
            traceback.print_exc()
            return None
#-----------------------------------------------------------------------
# Dialog Window
#-----------------------------------------------------------------------
class MUIDialogWindow(MUIWindow, PopupBase):
    """MUIDialogWindow is a window to support popup window
    """
    def __init__(self, *args, **kwargs):
        self.owner = kwargs.get('owner', None)
        dxDevice = None
        if (self.owner != None):
            dxDevice = self.owner.dxDevice
            kwargs.setdefault('dxDevice', dxDevice)
        MUIWindow.__init__(self, *args, **kwargs)
        PopupBase.__init__(self)



    def _create_w32window(self, **kwargs):
        try:
            kwargs.setdefault('windowStyle', WS_SYSMENU)
            kwargs.setdefault('windowExStyle', 0)

            super(MUIDialogWindow, self)._create_w32window(**kwargs)

        except Exception, e:
               traceback.print_exc()
               return None

    def closePopup(self):
        """ close or cancel popup control after duration
        """
        self.invoke('Close Popup')
        self.blind = True
        if self.closeDuration > 0:
            anim.schedule_once( self.DestoryWindow, self.closeDuration)
        else:
            anim.postExecute(self.DestroyWindow)
        self.__enableOwner(True)
        self.owner.setFocus()

    def close(self):
        super(MUIDialogWindow, self).close()
        self.doBreakLoop()

    def on_close(self):
        self.__enableOwner(True)
        super(MUIDialogWindow, self).on_close()

    def __enableOwner(self, bEnable):
        if self.owner:
            self.owner.EnableWindow(bEnable)

    def doModal(self):
        self.__enableOwner(False)

        assert self.breakLoop
        self.retValue = False
        self.breakLoop = False
        self.setFocus()
        self.show()
        self.hideToolTips()
        muiApp.run_domodal(self)

        return self.retValue
